﻿using System.Xml;
using System.ComponentModel;
using System.ServiceModel;
using System.Configuration;
using System.ServiceModel.Channels;
using System.ServiceModel.Configuration;
using System.ServiceModel.Description;
using System.Runtime.Serialization;
using System;

namespace Tipler.CompressionEncoder
{
    internal sealed class CompressionMessageEncodingPolicyConstants
    {
        public const string CompressionEncodingName = "CompressionEncoding";
        public const string CompressionEncodingNamespace = "http://schemas.microsoft.com/ws/06/2004/mspolicy/netgzip1";
        public const string CompressionEncodingPrefix = "compression";
    }

    //GZip encodingi enable eden binding element
    public sealed class CompressionMessageEncodingBindingElement : MessageEncodingBindingElement, IPolicyExportExtension
    {
        private MessageEncodingBindingElement innerBindingElement;

        public MessageEncodingBindingElement InnerMessageEncodingBindingElement
        {
            get { return this.innerBindingElement; }
            set { this.innerBindingElement = value; }
        }

        public override MessageVersion MessageVersion
        {
            get { return this.innerBindingElement.MessageVersion; }
            set { this.innerBindingElement.MessageVersion = value; }
        }

        //'By default, use the default text encoder as the inner encoder
        public CompressionMessageEncodingBindingElement()
            : this(new BinaryMessageEncodingBindingElement())
        {
        }

        public CompressionMessageEncodingBindingElement(MessageEncodingBindingElement messageEncoderBindingElement)
        {
            this.innerBindingElement = messageEncoderBindingElement;
        }

        public override IChannelFactory<TChannel> BuildChannelFactory<TChannel>(BindingContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }
            context.BindingParameters.Add(this);
            return context.BuildInnerChannelFactory<TChannel>();
        }

        public override IChannelListener<TChannel> BuildChannelListener<TChannel>(BindingContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }
            context.BindingParameters.Add(this);
            return context.BuildInnerChannelListener<TChannel>();
        }

        public override bool CanBuildChannelListener<TChannel>(BindingContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }
            context.BindingParameters.Add(this);
            return context.CanBuildInnerChannelListener<TChannel>();
        }

        public override BindingElement Clone()
        {
            return new CompressionMessageEncodingBindingElement(this.innerBindingElement);
        }

        //'Main entry point into the encoder binding element. Called by WCF to get the factory that will create the
        //'message encoder
        public override MessageEncoderFactory CreateMessageEncoderFactory()
        {
            return new CompressionMessageEncoderFactory(this.innerBindingElement.CreateMessageEncoderFactory());
        }


        public override T GetProperty<T>(BindingContext context)
        {
            if ((object.ReferenceEquals(typeof(T), typeof(XmlDictionaryReaderQuotas))))
            {
                return this.innerBindingElement.GetProperty<T>(context);
            }

            return base.GetProperty<T>(context);
        }

        #region IPolicyExportExtension Members

        public void ExportPolicy(MetadataExporter exporter, PolicyConversionContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException("policyContext");
            }
            XmlDocument document = new XmlDocument();
            context.GetBindingAssertions().Add(document.CreateElement(
            CompressionMessageEncodingPolicyConstants.CompressionEncodingPrefix,
            CompressionMessageEncodingPolicyConstants.CompressionEncodingName,
            CompressionMessageEncodingPolicyConstants.CompressionEncodingNamespace)); 
        }

        #endregion

    }
    public class CompressionMessageEncodingElement : BindingElementExtensionElement
    {
        public override Type BindingElementType
        {
            get { return typeof(CompressionMessageEncodingBindingElement); }
        }

        //'The only property we need to configure for our binding element is the type of
        //'inner encoder to use. Here, we support text and binary.
        [ConfigurationProperty("innerMessageEncoding", DefaultValue = "textMessageEncoding")]
        public string InnerMessageEncoding
        {
            get { return base["innerMessageEncoding"].ToString(); }
            set { base["innerMessageEncoding"] = value; }
        }

        //'Called by the WCF to apply the configuration settings (the property above) to the binding element
        public override void ApplyConfiguration(BindingElement bindingElement)
        {
            CompressionMessageEncodingBindingElement binding = (CompressionMessageEncodingBindingElement)bindingElement;

            PropertyInformationCollection propertyInfo = this.ElementInformation.Properties;
            if (base.ElementInformation.Properties["innerMessageEncoding"].ValueOrigin != PropertyValueOrigin.Default)
            {
                switch (this.InnerMessageEncoding)
                {
                    case "textMessageEncoding":
                        binding.InnerMessageEncodingBindingElement = new TextMessageEncodingBindingElement();
                        ((TextMessageEncodingBindingElement)binding.InnerMessageEncodingBindingElement).ReaderQuotas.MaxDepth = int.MaxValue;
                        ((TextMessageEncodingBindingElement)binding.InnerMessageEncodingBindingElement).ReaderQuotas.MaxStringContentLength = int.MaxValue;
                        ((TextMessageEncodingBindingElement)binding.InnerMessageEncodingBindingElement).ReaderQuotas.MaxNameTableCharCount = int.MaxValue;
                        ((TextMessageEncodingBindingElement)binding.InnerMessageEncodingBindingElement).ReaderQuotas.MaxBytesPerRead = int.MaxValue;
                        ((TextMessageEncodingBindingElement)binding.InnerMessageEncodingBindingElement).ReaderQuotas.MaxArrayLength = int.MaxValue;

                        break;
                    case "binaryMessageEncoding":
                        binding.InnerMessageEncodingBindingElement = new BinaryMessageEncodingBindingElement();
                        ((BinaryMessageEncodingBindingElement)binding.InnerMessageEncodingBindingElement).ReaderQuotas.MaxDepth = int.MaxValue;
                        ((BinaryMessageEncodingBindingElement)binding.InnerMessageEncodingBindingElement).ReaderQuotas.MaxStringContentLength = int.MaxValue;
                        ((BinaryMessageEncodingBindingElement)binding.InnerMessageEncodingBindingElement).ReaderQuotas.MaxNameTableCharCount = int.MaxValue;
                        ((BinaryMessageEncodingBindingElement)binding.InnerMessageEncodingBindingElement).ReaderQuotas.MaxBytesPerRead = int.MaxValue;
                        ((BinaryMessageEncodingBindingElement)binding.InnerMessageEncodingBindingElement).ReaderQuotas.MaxArrayLength = int.MaxValue;
                        break;
                }
            }
        }


        //'Called by the WCF to create the binding element
        protected override BindingElement CreateBindingElement()
        {
            CompressionMessageEncodingBindingElement bindingElement = new CompressionMessageEncodingBindingElement();
            
            this.ApplyConfiguration(bindingElement);
            return bindingElement;
        }

        public void ExportPolicy(MetadataExporter exporter, PolicyConversionContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException("policyContext");
            }
            XmlDocument document = new XmlDocument();
            context.GetBindingAssertions().Add(document.CreateElement(
            CompressionMessageEncodingPolicyConstants.CompressionEncodingPrefix,
            CompressionMessageEncodingPolicyConstants.CompressionEncodingName,
            CompressionMessageEncodingPolicyConstants.CompressionEncodingNamespace));
        }


    }
}
